{% extends 'base.html' %}
{% load static %}

{% block title %}知识图谱 - 机械臂实验系统{% endblock %}

{% block extra_css %}
<style>
    #knowledge-graph-container {
        width: 100%;
        height: 800px;
        background-color: #f9f9f9;
        border-radius: 8px;
        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        overflow: hidden;
        position: relative;
    }
    
    .node {
        cursor: pointer;
    }
    
    .node circle {
        fill: #fff;
        stroke-width: 2px;
    }
    
    .node text {
        font: 12px sans-serif;
        fill: #333;
        font-weight: 500;
    }
    
    .link {
        fill: none;
        stroke-width: 1.5px;
    }
    
    .tooltip {
        position: absolute;
        background-color: rgba(255, 255, 255, 0.95);
        border-radius: 5px;
        padding: 10px;
        font-size: 14px;
        max-width: 400px;
        box-shadow: 0 1px 8px rgba(0, 0, 0, 0.2);
        pointer-events: none;
        opacity: 0;
        transition: opacity 0.2s;
        z-index: 100;
        color: #333;
        border: 1px solid #ddd;
    }
    
    .node-central circle {
        fill: #ff9500;
    }
    
    .node-category circle {
        fill: #4285f4;
    }
    
    .node-subcategory circle {
        fill: #34a853;
    }
    
    .node-detail circle {
        fill: #ea4335;
    }
    
    .node-subdetail circle {
        fill: #fbbc05;
    }
    
    .node:hover circle {
        stroke-width: 4px;
    }
    
    .controls {
        position: absolute;
        top: 20px;
        right: 20px;
        background: rgba(255, 255, 255, 0.9);
        border-radius: 5px;
        padding: 10px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
        z-index: 10;
    }
    
    .btn-control {
        margin: 3px;
        padding: 5px 10px;
        font-size: 14px;
    }
    
    .node-highlight circle {
        stroke: #ff5722 !important;
        stroke-width: 3px !important;
    }
    
    .node-label-box {
        fill: rgba(255, 255, 255, 0.8);
        rx: 4;
        ry: 4;
    }
    
    /* 智能助手样式 */
    #chat-container {
        position: fixed;
        bottom: 20px;
        right: 20px;
        width: 350px;
        height: 500px;
        background-color: #fff;
        border-radius: 10px;
        box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
        display: flex;
        flex-direction: column;
        z-index: 1000;
        overflow: hidden;
        transition: all 0.3s ease;
    }
    
    #chat-container.collapsed {
        height: 60px;
    }
    
    #chat-header {
        padding: 15px;
        background-color: #4285f4;
        color: white;
        font-weight: bold;
        display: flex;
        justify-content: space-between;
        align-items: center;
        cursor: pointer;
    }
    
    #chat-header i {
        transform: rotate(0deg);
        transition: transform 0.3s ease;
    }
    
    #chat-container.collapsed #chat-header i {
        transform: rotate(180deg);
    }
    
    #chat-messages {
        flex: 1;
        overflow-y: auto;
        padding: 15px;
        display: flex;
        flex-direction: column;
        gap: 10px;
    }
    
    .message {
        max-width: 80%;
        padding: 10px 15px;
        border-radius: 18px;
        margin-bottom: 5px;
        word-wrap: break-word;
        position: relative;
        animation: fadeIn 0.3s ease;
    }
    
    @keyframes fadeIn {
        from { opacity: 0; transform: translateY(10px); }
        to { opacity: 1; transform: translateY(0); }
    }
    
    .user-message {
        background-color: #e6f2ff;
        align-self: flex-end;
        border-bottom-right-radius: 5px;
    }
    
    .assistant-message {
        background-color: #f1f1f1;
        align-self: flex-start;
        border-bottom-left-radius: 5px;
    }
    
    #chat-input-container {
        display: flex;
        padding: 10px;
        border-top: 1px solid #eee;
    }
    
    #chat-input {
        flex: 1;
        padding: 10px;
        border: 1px solid #ddd;
        border-radius: 20px;
        outline: none;
    }
    
    #chat-send {
        background-color: #4285f4;
        color: white;
        border: none;
        border-radius: 50%;
        width: 40px;
        height: 40px;
        margin-left: 10px;
        cursor: pointer;
        display: flex;
        justify-content: center;
        align-items: center;
    }
    
    #chat-send:hover {
        background-color: #3367d6;
    }
    
    .typing-indicator {
        display: flex;
        align-items: center;
        padding: 10px 15px;
        background-color: #f1f1f1;
        align-self: flex-start;
        border-radius: 18px;
        border-bottom-left-radius: 5px;
        margin-bottom: 5px;
    }
    
    .typing-dot {
        background-color: #666;
        border-radius: 50%;
        width: 8px;
        height: 8px;
        margin: 0 2px;
        animation: typing 1.4s infinite both;
    }
    
    .typing-dot:nth-child(2) {
        animation-delay: 0.2s;
    }
    
    .typing-dot:nth-child(3) {
        animation-delay: 0.4s;
    }
    
    @keyframes typing {
        0% { transform: translateY(0); }
        50% { transform: translateY(-10px); }
        100% { transform: translateY(0); }
    }
</style>
{% endblock %}

{% block content %}
<div class="container mt-4">
    <div class="row">
        <div class="col-12">
            <div class="card">
                <div class="card-header bg-primary text-white">
                    <h4 class="mb-0"><i class="bi bi-diagram-3"></i> 多模态智能螺丝拆卸机器人系统 - 知识图谱</h4>
                </div>
                <div class="card-body p-0">
                    <div id="knowledge-graph-container">
                        <div class="controls">
                            <button class="btn btn-sm btn-outline-primary btn-control" id="btn-zoom-in"><i class="bi bi-zoom-in"></i> 放大</button>
                            <button class="btn btn-sm btn-outline-primary btn-control" id="btn-zoom-out"><i class="bi bi-zoom-out"></i> 缩小</button>
                            <button class="btn btn-sm btn-outline-primary btn-control" id="btn-reset"><i class="bi bi-arrow-counterclockwise"></i> 重置</button>
                            <button class="btn btn-sm btn-outline-primary btn-control" id="btn-expand-all">展开全部</button>
                            <button class="btn btn-sm btn-outline-primary btn-control" id="btn-collapse-all">收起全部</button>
                        </div>
                        <div class="tooltip" id="tooltip"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- DeepSeek智能助手聊天窗口 -->
<div id="chat-container">
    <div id="chat-header">
        <span>DeepSeek智能助手</span>
        <i class="bi bi-chevron-up"></i>
    </div>
    <div id="chat-messages">
        <div class="message assistant-message">
            您好！我是DeepSeek智能助手，我可以帮助您了解多模态智能螺丝拆卸机器人系统的各个方面。有什么我可以帮您解答的吗？
        </div>
    </div>
    <div id="chat-input-container">
        <input type="text" id="chat-input" placeholder="请输入您的问题..." />
        <button id="chat-send"><i class="bi bi-send"></i></button>
    </div>
</div>
{% endblock %}

{% block extra_js %}
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
    // 知识图谱数据
    const knowledgeData = {
        name: "多模态智能螺丝拆卸机器人系统",
        children: [
            {
                name: "算法突破",
                children: [
                    {
                        name: "CNN-LSTM混合模型",
                        children: [
                            { name: "抗偏心误差<=3mm" },
                            { name: "预处理：滤波/截断/归一化" },
                            { name: "输入：轴向力，Fz时序数据" },
                            { name: "序列长度=70点" },
                            { name: "输出：最佳升速Vr" }
                        ]
                    }
                ]
            },
            {
                name: "工具创新",
                children: [
                    {
                        name: "弹簧针阵式通用套筒",
                        children: [
                            { name: "兼容性上升60％" },
                            { name: "适配M3/M4/M5等螺丝" },
                            { name: "弹性钢针阵列" },
                            { name: "工具切换时间=0" },
                            { name: "兼容六角/十字/梅花头" }
                        ]
                    }
                ]
            },
            {
                name: "虚拟仿真平台",
                children: [
                    { name: "实时力数据可视化" },
                    {
                        name: "参数自由调整",
                        children: [
                            { name: "螺栓规格/转速/升速" }
                        ]
                    },
                    {
                        name: "故障模拟教学",
                        children: [
                            { name: "碎屑掉落/速度冲突" }
                        ]
                    },
                    {
                        name: "B/S架构",
                        children: [
                            { name: "前端：Three.js/WebGL" },
                            { name: "后端：Django+MySQL" }
                        ]
                    }
                ]
            },
            {
                name: "数据驱动设计",
                children: [
                    {
                        name: "传感器数据流",
                        children: [
                            { name: "100Hz采样频率" },
                            { name: "FT300力传感器" }
                        ]
                    },
                    {
                        name: "鲁棒性增强",
                        children: [
                            { name: "8阶Butterworth滤波" },
                            { name: "Fz范围：-40N至-5N" },
                            { name: "序列填充/修剪" }
                        ]
                    }
                ]
            },
            {
                name: "机械臂系统",
                children: [
                    {
                        name: "UR5六轴机械臂",
                        children: [
                            {
                                name: "接近",
                                children: [
                                    { name: "cv技术" }
                                ]
                            },
                            {
                                name: "搜索和啮合",
                                children: [
                                    { name: "螺旋搜索策略" },
                                    { name: "深度强化学习" }
                                ]
                            },
                            {
                                name: "拆卸和评估",
                                children: [
                                    {
                                        name: "三阶段控制逻辑",
                                        children: [
                                            { name: "线性阶段：机器人静止，表现得像弹簧，接触力Fz线性增加" },
                                            { name: "稳定阶段：机器人和套筒同步上升，接触力Fz不变" },
                                            { name: "终止信号确定阶段：接触力Fz归零" }
                                        ]
                                    }
                                ]
                            },
                            { name: "离开" }
                        ]
                    }
                ]
            }
        ]
    };

    // 设置SVG
    const width = document.getElementById('knowledge-graph-container').offsetWidth;
    const height = document.getElementById('knowledge-graph-container').offsetHeight;
    
    // 创建主SVG元素
    const svg = d3.select('#knowledge-graph-container')
        .append('svg')
        .attr('width', width)
        .attr('height', height);
        
    // 添加缩放行为
    const zoomHandler = d3.zoom()
        .scaleExtent([0.3, 2])
        .on('zoom', (event) => {
            g.attr('transform', event.transform);
        });
    
    svg.call(zoomHandler);
    
    // 创建主容器g元素，所有其他元素将添加到此g元素
    const g = svg.append('g')
        .attr('transform', `translate(${width / 2}, ${height / 2})`);
    
    // 初始缩放以适应屏幕
    // svg.call(zoomHandler.transform, d3.zoomIdentity.translate(width / 2, height / 2).scale(0.8));
    
    // 创建力导向图布局
    const simulation = d3.forceSimulation()
        .force('link', d3.forceLink().id(d => d.id).distance(d => 120))
        .force('charge', d3.forceManyBody().strength(-800))
        .force('center', d3.forceCenter(0, 0))
        .force('collision', d3.forceCollide().radius(d => getNodeRadius(d) * 1.5));
    
    // 处理数据
    const root = d3.hierarchy(knowledgeData);
    let nodes = root.descendants();
    let links = root.links();
    
    // 为每个节点分配ID和深度级别
    nodes.forEach((node, i) => {
        node.id = i;
        node.depth = node.depth;
        node._children = node.children;
        if (node.depth > 1) {
            node.children = null; // 初始只展开两层
        }
    });
    
    // 获取节点半径的函数
    function getNodeRadius(d) {
        if (d.depth === 0) return 50;
        else if (d.depth === 1) return 40;
        else if (d.depth === 2) return 35;
        else if (d.depth === 3) return 30;
        else return 25;
    }
    
    // 更新图形函数
    function update() {
        // 重建活动链接和节点
        links = root.links();
        nodes = root.descendants();
        
        // 更新模拟
        simulation.nodes(nodes);
        simulation.force('link').links(links);
        simulation.alpha(1).restart();
        
        // 更新链接
        const link = g.selectAll('.link')
            .data(links, d => d.target.id);
        
        link.exit().remove();
        
        const linkEnter = link.enter()
            .append('path')
            .attr('class', 'link')
            .style('stroke', '#999')
            .style('stroke-opacity', 0.6);
        
        // 合并链接选择
        const linkMerge = linkEnter.merge(link);
        
        // 更新节点
        const node = g.selectAll('.node')
            .data(nodes, d => d.id);
        
        node.exit().remove();
        
        // 创建新节点
        const nodeEnter = node.enter()
            .append('g')
            .attr('class', d => {
                let nodeClass = 'node';
                if (d.depth === 0) nodeClass += ' node-central';
                else if (d.depth === 1) nodeClass += ' node-category';
                else if (d.depth === 2) nodeClass += ' node-subcategory';
                else if (d.depth === 3) nodeClass += ' node-detail';
                else nodeClass += ' node-subdetail';
                return nodeClass;
            })
            .on('click', toggleNode)
            .on('mouseover', showTooltip)
            .on('mousemove', moveTooltip)
            .on('mouseout', hideTooltip)
            .call(d3.drag()
                .on('start', dragStarted)
                .on('drag', dragged)
                .on('end', dragEnded));
        
        // 添加圆形
        nodeEnter.append('circle')
            .attr('r', getNodeRadius)
            .style('stroke', d => {
                if (d.depth === 0) return '#e65100';
                else if (d.depth === 1) return '#0d47a1';
                else if (d.depth === 2) return '#1b5e20';
                else if (d.depth === 3) return '#b71c1c';
                else return '#f57f17';
            });
        
        // 为长文本添加背景矩形
        nodeEnter.append('rect')
            .attr('class', 'node-label-box')
            .attr('x', -60)
            .attr('y', d => getNodeRadius(d) + 5)
            .attr('width', 120)
            .attr('height', 20)
            .attr('opacity', 0.8);
            
        // 添加文本标签
        nodeEnter.append('text')
            .attr('dy', d => getNodeRadius(d) + 20)
            .style('text-anchor', 'middle')
            .style('pointer-events', 'none')
            .style('font-size', d => {
                if (d.depth === 0) return '16px';
                else if (d.depth === 1) return '14px';
                else return '12px';
            })
            .style('font-weight', d => d.depth <= 1 ? 'bold' : 'normal')
            .text(d => {
                // 针对不同长度的文本进行处理
                const name = d.data.name;
                if (name.length > 30) {
                    return name.substring(0, 27) + '...';
                } else if (name.length > 20) {
                    return name.substring(0, 17) + '...';
                } else if (name.length > 10) {
                    return name.substring(0, 9) + '...';
                } else {
                    return name;
                }
            });
        
        // 合并节点选择
        const nodeMerge = nodeEnter.merge(node);
        
        // 模拟刻度函数
        simulation.on('tick', () => {
            linkMerge.attr('d', d => {
                const dx = d.target.x - d.source.x;
                const dy = d.target.y - d.source.y;
                const dr = Math.sqrt(dx * dx + dy * dy) * 1.5;
                return `M${d.source.x},${d.source.y}A${dr},${dr} 0 0,1 ${d.target.x},${d.target.y}`;
            });
            
            nodeMerge.attr('transform', d => `translate(${d.x},${d.y})`);
        });
    }
    
    // 切换节点展开/折叠
    function toggleNode(event, d) {
        if (d.children) {
            d._children = d.children;
            d.children = null;
        } else if (d._children) {
            d.children = d._children;
        }
        update();
    }
    
    // 工具提示函数
    function showTooltip(event, d) {
        const tooltip = d3.select('#tooltip');
        tooltip.html(`<strong>${d.data.name}</strong>`)
            .style('left', (event.pageX + 10) + 'px')
            .style('top', (event.pageY - 20) + 'px')
            .style('opacity', 1);
    }
    
    function moveTooltip(event) {
        d3.select('#tooltip')
            .style('left', (event.pageX + 10) + 'px')
            .style('top', (event.pageY - 20) + 'px');
    }
    
    function hideTooltip() {
        d3.select('#tooltip')
            .style('opacity', 0);
    }
    
    // 拖动函数
    function dragStarted(event, d) {
        if (!event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
    }
    
    function dragged(event, d) {
        d.fx = event.x;
        d.fy = event.y;
    }
    
    function dragEnded(event, d) {
        if (!event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
    }
    
    // 控制按钮功能
    d3.select('#btn-zoom-in').on('click', function() {
        svg.transition().duration(300).call(
            zoomHandler.scaleBy, 1.3
        );
    });
    
    d3.select('#btn-zoom-out').on('click', function() {
        svg.transition().duration(300).call(
            zoomHandler.scaleBy, 0.7
        );
    });
    
    d3.select('#btn-reset').on('click', function() {
        svg.transition().duration(500).call(
            zoomHandler.transform, d3.zoomIdentity.translate(width / 2, height / 2).scale(1)
        );
    });
    
    d3.select('#btn-expand-all').on('click', function() {
        expandAll(root);
        update();
    });
    
    d3.select('#btn-collapse-all').on('click', function() {
        collapseAll(root);
        update();
    });
    
    function expandAll(d) {
        if (d._children) {
            d.children = d._children;
            d.children.forEach(expandAll);
        }
    }
    
    function collapseAll(d) {
        if (d.children) {
            d._children = d.children;
            d.children = null;
        }
        if (d._children) {
            d._children.forEach(collapseAll);
        }
    }
    
    // 初始化图表
    update();

    // 智能助手功能
    const chatHeader = document.getElementById('chat-header');
    const chatContainer = document.getElementById('chat-container');
    const chatMessages = document.getElementById('chat-messages');
    const chatInput = document.getElementById('chat-input');
    const chatSend = document.getElementById('chat-send');
    
    // 切换聊天窗口展开/折叠
    chatHeader.addEventListener('click', () => {
        chatContainer.classList.toggle('collapsed');
    });
    
    // 发送消息
    function sendMessage() {
        const message = chatInput.value.trim();
        if (message) {
            // 添加用户消息
            appendMessage(message, 'user');
            
            // 显示加载动画
            showTypingIndicator();
            
            // 清空输入框
            chatInput.value = '';
            
            // 发送到后端
            fetch('/experiment/deepseek_assistant/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRFToken': getCookie('csrftoken')
                },
                body: JSON.stringify({
                    message: message
                })
            })
            .then(response => response.json())
            .then(data => {
                // 隐藏加载动画
                hideTypingIndicator();
                
                // 添加助手回复
                if (data.reply) {
                    appendMessage(data.reply, 'assistant');
                } else if (data.error) {
                    appendMessage(`抱歉，发生了错误: ${data.error}`, 'assistant');
                }
            })
            .catch(error => {
                hideTypingIndicator();
                appendMessage('抱歉，连接服务器时出现问题，请稍后再试。', 'assistant');
                console.error('Error:', error);
            });
        }
    }
    
    // 添加消息到聊天窗口
    function appendMessage(text, sender) {
        const messageDiv = document.createElement('div');
        messageDiv.className = `message ${sender}-message`;
        messageDiv.textContent = text;
        chatMessages.appendChild(messageDiv);
        chatMessages.scrollTop = chatMessages.scrollHeight;
    }
    
    // 显示正在输入指示器
    function showTypingIndicator() {
        const indicator = document.createElement('div');
        indicator.className = 'typing-indicator';
        indicator.id = 'typing-indicator';
        
        for (let i = 0; i < 3; i++) {
            const dot = document.createElement('div');
            dot.className = 'typing-dot';
            indicator.appendChild(dot);
        }
        
        chatMessages.appendChild(indicator);
        chatMessages.scrollTop = chatMessages.scrollHeight;
    }
    
    // 隐藏正在输入指示器
    function hideTypingIndicator() {
        const indicator = document.getElementById('typing-indicator');
        if (indicator) {
            indicator.remove();
        }
    }
    
    // 获取CSRF令牌的函数
    function getCookie(name) {
        let cookieValue = null;
        if (document.cookie && document.cookie !== '') {
            const cookies = document.cookie.split(';');
            for (let i = 0; i < cookies.length; i++) {
                const cookie = cookies[i].trim();
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    
    // 发送按钮点击事件
    chatSend.addEventListener('click', sendMessage);
    
    // 输入框按下回车事件
    chatInput.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
            sendMessage();
        }
    });
});
</script>
{% endblock %} 